package lumis.portal.channel.template;

import lumis.portal.PortalException;
import lumis.portal.PortalObjectAlreadyExistsException;
import lumis.portal.PortalObjectNotFoundException;
import lumis.portal.UnexpectedException;
import lumis.portal.dao.*;
import lumis.portal.dao.jdbc.ITransactionJdbc;
import lumis.util.*;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import java.util.Vector;

/**
 * JDBC-based DAO implementation for channel template.
 *
 * @version $Revision: 11804 $ $Date: 2010-07-29 10:06:21 -0300 (Thu, 29 Jul 2010) $
 * @since 4.0.0
 */
class ChannelTemplateDaoJdbc
{
	public List<ChannelTemplateLinkConfig> getChannelLinks(String channelId, boolean getLinkedElementsOnly, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();
			PreparedStatement statement;

			Vector<ChannelTemplateLinkConfig> templateLinkConfigs = new Vector<ChannelTemplateLinkConfig>();

			String query = "select * from lum_ChannelTemplateLinks where destinationChannelId = ?";
			
			if(getLinkedElementsOnly)
				query += " and linked = 1";
			
			query += " order by itemType";
			
			statement = connection.prepareStatement(query);			
			try
			{
				statement.setString(1, channelId);
				
				ResultSet resultSet = statement.executeQuery();
				try
				{
					while (resultSet.next())
					{
						ChannelTemplateLinkConfig templateLinkConfig = new ChannelTemplateLinkConfig();

						templateLinkConfig.setId(resultSet.getString("linkId"));
						templateLinkConfig.setTemplateChannelId(resultSet.getString("templateChannelId"));
						templateLinkConfig.setDestinationChannelId(resultSet.getString("destinationChannelId"));
						templateLinkConfig.setSourceId(resultSet.getString("sourceId"));
						templateLinkConfig.setDestinationId(resultSet.getString("destinationId"));
						templateLinkConfig.setItemType(resultSet.getInt("itemType"));
						templateLinkConfig.setLinked(resultSet.getInt("linked") == 1);		
						
						templateLinkConfigs.add(templateLinkConfig);
					}
				}
				finally
				{
					resultSet.close();
				}
			}
			finally
			{
				statement.close();
			}

			return templateLinkConfigs;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	public ChannelTemplateLinkConfig getChannelLinkBySourceId(String channelId, String sourceId, boolean getLinkedElementsOnly, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();
			PreparedStatement statement;

			ChannelTemplateLinkConfig templateLinkConfig = null;

			String query = "select * from lum_ChannelTemplateLinks where destinationChannelId = ? and sourceId = ?";
			
			if(getLinkedElementsOnly)
				query += " and linked = 1";
			
			query += " order by itemType";
			
			statement = connection.prepareStatement(query);			
			try
			{
				statement.setString(1, channelId);
				statement.setString(2, sourceId);
				
				ResultSet resultSet = statement.executeQuery();
				try
				{
					if (resultSet.next())
					{
						templateLinkConfig = new ChannelTemplateLinkConfig();
						
						templateLinkConfig.setId(resultSet.getString("linkId"));
						templateLinkConfig.setTemplateChannelId(resultSet.getString("templateChannelId"));
						templateLinkConfig.setDestinationChannelId(resultSet.getString("destinationChannelId"));
						templateLinkConfig.setSourceId(resultSet.getString("sourceId"));
						templateLinkConfig.setDestinationId(resultSet.getString("destinationId"));
						templateLinkConfig.setItemType(resultSet.getInt("itemType"));
						templateLinkConfig.setLinked(resultSet.getInt("linked") == 1);		
					}
				}
				finally
				{
					resultSet.close();
				}
			}
			finally
			{
				statement.close();
			}

			return templateLinkConfig;
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	public ChannelTemplateLinkConfig getChannelLink(String channelLinkId, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();
			PreparedStatement statement;

			String query = "select * from lum_ChannelTemplateLinks where linkId = ?";
			statement = connection.prepareStatement(query);			
			try
			{
				statement.setString(1, channelLinkId);
				
				ResultSet resultSet = statement.executeQuery();
				try
				{
					if (resultSet.next())
					{
						ChannelTemplateLinkConfig templateLinkConfig = new ChannelTemplateLinkConfig();

						templateLinkConfig.setId(resultSet.getString("linkId"));
						templateLinkConfig.setTemplateChannelId(resultSet.getString("templateChannelId"));
						templateLinkConfig.setDestinationChannelId(resultSet.getString("destinationChannelId"));
						templateLinkConfig.setSourceId(resultSet.getString("sourceId"));
						templateLinkConfig.setDestinationId(resultSet.getString("destinationId"));
						templateLinkConfig.setItemType(resultSet.getInt("itemType"));
						templateLinkConfig.setLinked(resultSet.getInt("linked") == 1);		
						
						return templateLinkConfig;
					}
					else
					{
						throw new PortalObjectNotFoundException("ChannelTemplateLinkConfig with id='" + channelLinkId + "'");
					}
				}
				finally
				{
					resultSet.close();
				}
			}
			finally
			{
				statement.close();
			}
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Adds or updates a {@link ChannelTemplateLinkConfig}.
	 * @param channelTemplateLinkConfig the link to be added or updated.
	 * @param transaction the transaction for persistence access.
	 * @since 5.6.0
	 */
	public void addOrUpdateChannelLink(ChannelTemplateLinkConfig channelTemplateLinkConfig, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();
			
			ChannelTemplateLinkConfig originalChannelTemplateLinkConfig;
			try
			{
				originalChannelTemplateLinkConfig = getChannelLink(channelTemplateLinkConfig.getId(), transaction);
			
				// if not changed, skip update (do nothing)
				if(channelTemplateLinkConfig.isIdentical(originalChannelTemplateLinkConfig))
					return;
				
				PreparedStatement statement = connection.prepareStatement("update lum_ChannelTemplateLinks set templateChannelId=?, destinationChannelId=?, sourceId=?, destinationId=?, itemType=?, linked=? where linkId=?");
				try
				{
					statement.setString(1, channelTemplateLinkConfig.getTemplateChannelId());
					statement.setString(2, channelTemplateLinkConfig.getDestinationChannelId());
					statement.setString(3, channelTemplateLinkConfig.getSourceId());
					statement.setString(4, channelTemplateLinkConfig.getDestinationId());
					statement.setInt(5, channelTemplateLinkConfig.getItemType());
					statement.setBoolean(6, channelTemplateLinkConfig.isLinked());
					statement.setString(7, channelTemplateLinkConfig.getId());
					statement.executeUpdate();
				}
				finally
				{
					statement.close();
				}
			}
			catch(PortalObjectNotFoundException e)
			{
				// does not exist, so add it
				addChannelLink(channelTemplateLinkConfig, transaction);
			}
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	public void addChannelLink(ChannelTemplateLinkConfig channelTemplateLinkConfig, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();

			PreparedStatement statement = connection.prepareStatement("insert into lum_ChannelTemplateLinks (linkId, templateChannelId, destinationChannelId, sourceId, destinationId, itemType, linked) values (?, ?, ?, ?, ?, ?, ?)");
			try
			{
				statement.setString(1, channelTemplateLinkConfig.getId());
				statement.setString(2, channelTemplateLinkConfig.getTemplateChannelId());
				statement.setString(3, channelTemplateLinkConfig.getDestinationChannelId());
				statement.setString(4, channelTemplateLinkConfig.getSourceId());
				statement.setString(5, channelTemplateLinkConfig.getDestinationId());
				statement.setInt(6, channelTemplateLinkConfig.getItemType());
				statement.setBoolean(7, channelTemplateLinkConfig.isLinked());
				
				try
				{
					statement.executeUpdate();
				}
				catch (Exception e)
				{
					try
					{
						if(getChannelLink(channelTemplateLinkConfig.getId(), transaction) != null)
							throw new PortalObjectAlreadyExistsException("STR_RESOURCE_ALREADY_EXISTS;" + channelTemplateLinkConfig.getId(), e);
						else
							throw e;
					}
					catch (PortalObjectNotFoundException e1)
					{
						throw e;
					}
				}
			}
			finally
			{
				statement.close();
			}
		}
		catch (PortalException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	public void deleteChannelLink(String channelLinkId, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();

			PreparedStatement statement = connection.prepareStatement("delete from lum_ChannelTemplateLinks where linkId = ?");
			try
			{
				statement.setString(1, channelLinkId);
				statement.executeUpdate();				
			}
			finally
			{
				statement.close();				
			}
		}
		catch (DaoException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	public void deleteChannelLinks(String channelId, ITransaction transaction) throws DaoException, PortalException
	{
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();

			PreparedStatement statement = connection.prepareStatement("delete from lum_ChannelTemplateLinks where destinationChannelId = ?");
			try
			{
				statement.setString(1, channelId);
				statement.executeUpdate();				
			}
			finally
			{
				statement.close();				
			}
		}
		catch (DaoException e)
		{
			throw e;
		}
		catch (Exception e)
		{
			throw new UnexpectedException(e);
		}
	}
	
	/**
	 * Deletes channel links that refer to inexistent objects. 
	 * @param destinationChannelId the identifier of the channel where the 
	 * template is applied, whose unused template links are to be deleted.
	 * @param transaction the transaction for persistence access.
	 * @since 4.2.0
	 */
	public void deleteUnusedChannelLinks(String destinationChannelId, ITransaction transaction) throws PortalException
	{
		// TODO: currently deletes only channelLinks to channels. Ideally,
		// channelTemplate should listen to events and delete channelLinks when
		// the corresponding objects are deleted.
		try
		{
			ITransactionJdbc daoTransactionJdbc = (ITransactionJdbc) transaction;
			Connection connection = daoTransactionJdbc.getConnection();

			PreparedStatement statement = connection.prepareStatement(
					"delete from lum_ChannelTemplateLinks where destinationChannelId = ? and " +
					"itemType = 2 and destinationId not in (select channelId from lum_Channel)");
			try
			{
				statement.setString(1, destinationChannelId);
				statement.executeUpdate();
			}
			finally
			{
				statement.close();
			}
		}
		catch (SQLException e)
		{
			throw new UnexpectedException(e);
		}
	}
	
}